package com.auxiliary.indexCaculate;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;

///$Id: BasicCalculator.cs 1728 2007-12-09 02:56:29Z zhangxun $
/// <summary>
/// <author>zhangxun</author>
/// <date>20070804</date>
/// 基本运算类
/// 提供基本运算功能
/// </summary>
public class BasicCalculator
{

    /// <summary>
    /// 构造函数
    /// </summary>
    public BasicCalculator()
    {            
    }

    public static final double errorValue = -9999999.9;

    /// <summary>
    /// 求最大值 //added by shangwei@20071205
    /// </summary>
    /// <param name="dblArrayList"></param>
    /// <returns></returns>
    public Double GetMaxValue(ArrayList<Double> dblArrayList)
    {
        Double max = Double.MIN_VALUE;
        for (Double d : dblArrayList)
            max = Math.max(max, d);
        return max;
    }
    /// <summary>
    /// 求最小值 //added by shangwei@20071205
    /// </summary>
    /// <param name="dblArrayList"></param>
    /// <returns></returns>
    public Double GetMinValue(ArrayList<Double> dblArrayList)
    {
        Double min = Double.MAX_VALUE;
        for (Double d : dblArrayList)
            min = Math.min(min, d);
        return min;
    }
   
    /// <summary>
    /// 求和
    /// </summary>
    /// <param name="array">输入数组</param>
    /// <returns></returns>
    public Double CalculateSum(ArrayList<Double> array)
    {  
       Double sum = 0.0;
       for(Double d : array)
                  sum += d;        
        return sum;
    }

    /// <summary>
    /// 均值
    /// </summary>
    /// <param name="array">输入数组</param>
    /// <returns></returns>
    public Double CalculateMean(ArrayList<Double> array)
    {
        Double mean = 0.0;
        for (Double d : array)
            mean += d;
        mean = mean / array.size();
        return mean;
    }

    /// <summary>
    /// 标准差
    /// </summary>
    /// <param name="array">输入数组</param>
    /// <returns></returns>
    public Double CalculateStdev(ArrayList<Double> array)
   {
        Double stdev= 0.0;
        Double mean = this.CalculateMean(array);
        Double sumSqr = 0.0;
        for (Double d : array)
            sumSqr += (d-mean)*(d-mean);
        stdev = Math.sqrt(sumSqr /( array.size()-1));
        return stdev;
    }
    /// <summary>
    /// 计算中位数
    /// </summary>
    /// <param name="array">输入数组</param>
    /// <returns></returns>
    public Double CalculateMedian(ArrayList<Double> dataArrayList)
    {
    	int len = dataArrayList.size();
    	double mid = 0;
    	Collections.sort(dataArrayList);
    	if(len%2==0) mid = (dataArrayList.get(len/2)+dataArrayList.get(len/2+1))/2; 
    	else mid = dataArrayList.get(len/2);
    	return mid;
    }

    /// <summary>
    /// 计算偏度
    /// </summary>
    /// <param name="array"></param>
    /// <returns></returns>
    public Double CalculateSkewness(ArrayList<Double> array)
    {
        //if (array.size() == 1) return array.get(0);
        if (array.size() == 0) return errorValue;
        Double sd = CalculateStdev(array);
        Double mean = CalculateMean(array);
        Double sumCub = 0.0;
        for (Double d : array)
            sumCub += (d - mean) * (d - mean)*(d-mean);
        return (sumCub / (array.size() - 1))/ (sd*sd*sd) ;
        
    }

    /// <summary>
    /// 计算峰度
    /// </summary>
    /// <param name="array"></param>
    /// <returns></returns>
    public Double CalculateKurtosis(ArrayList<Double> array)
    {
        if (array.size() == 0) return errorValue;
        Double sd = CalculateStdev(array);
        Double mean = CalculateMean(array);
        Double sumCub = 0.0;
        for (Double d : array)
            sumCub += (d - mean) * (d - mean) * (d - mean)*(d-mean);
        return (sumCub / (array.size() - 1)) / (sd * sd * sd*sd)-3;
    }




    /// <summary>
    /// spencer移动平均程序
    /// </summary>
    /// <param name="array"><输入数组/param>
    /// <returns></returns>
    public ArrayList<Double> DoSpencer(ArrayList<Double> dataArrayList)
    {
        Double[] array = (Double[]) dataArrayList.toArray();
        int ArrayLength1 = array.length;
        Double[] Array1 = new Double[ArrayLength1 + 14];
        Double[] SpnArray1 = new Double[ArrayLength1];

        for (int i = 0; i < 7; i++) //将序列的长度加15
        {
            Array1[i] = (array[1] + array[2] + array[3] + array[0]) / 4;
            Array1[ArrayLength1 + 7 + i] = (array[ArrayLength1 - 1] + array[ArrayLength1 - 2] + array[ArrayLength1 - 3] + array[ArrayLength1 - 4]) / 4;
        }
        for (int i = 0; i < ArrayLength1; i++)
        {
            Array1[7 + i] = array[i];
        }
        for (int i = 0; i < ArrayLength1; i++)
        {
            SpnArray1[i] = (-3 * Array1[i] - 6 * Array1[i + 1] - 5 * Array1[i + 2] + 3 * Array1[i + 3] + 21 * Array1[i + 4] + 46 * Array1[i + 5] + 67 * Array1[i + 6] + 74 * Array1[i + 7] + 67 * Array1[i + 8] + 46 * Array1[i + 9] + 21 * Array1[i + 10] + 3 * Array1[i + 11] - 5 * Array1[i + 12] - 6 * Array1[i + 13] - 3 * Array1[i + 14]) / 320;
        }
        return  new ArrayList<Double>(Arrays.asList(SpnArray1));
    }

   
  /// <summary>
  /// 移动平均
  /// 移动平均阶数应大于0。如果输入阶数小于等于0，返回输入序列。
  /// </summary>
  /// <param name="order">移动平均阶数</param>
  /// <param name="dataArrayList">输入序列</param>
  /// <returns></returns>
    public ArrayList<Double> MovingAverage(int order,ArrayList<Double> dataArrayList )
    {
        if (order <= 0) return dataArrayList;
        Double[] array = (Double[]) dataArrayList.toArray();           
        Double a=(order-1.0)/2.0;
        int halfOrder =(int) Math.floor(a);
        boolean isEvenOrder = false; //是否是偶数阶
        if (order / 2 - halfOrder > 0) { halfOrder += 1; isEvenOrder = true; }
        
        int arrayLength=array.length;
        Double[] extendArray = new Double[arrayLength + order];
        Double[] movingArray = new Double[arrayLength];
        extendArray[0] = 0.0;
        for (int i = 0; i < halfOrder; i++) //增加序列长度
        {
            for (int j = 0; j < halfOrder; j++)
            {
            	extendArray[i] += array[j];
                extendArray[arrayLength + i+halfOrder] += array[arrayLength -halfOrder+j]; 
            }
            extendArray[i] = extendArray[i] / halfOrder;
            extendArray[arrayLength + i+ halfOrder] = extendArray[arrayLength + halfOrder + i] / halfOrder;
        }
        for (int i = 0; i < arrayLength; i++)
        {
            extendArray[halfOrder + i] = extendArray[i];
        }
        if (isEvenOrder)
        {
            for (int i = 0; i < arrayLength; i++)
            {
                for (int j = 0; j < order; j++)
                    if (j == 0 || j == order)
                        movingArray[i] += extendArray[i + j] / 2;
                    else
                        movingArray[i] += extendArray[i + j];
                movingArray[i] = movingArray[i] / order;
            }
        }
        else
        {
            for (int i = 0; i < arrayLength; i++)
            {
                for (int j = 0; j < order; j++)
                    movingArray[i] += extendArray[i + j];
                movingArray[i] = movingArray[i] / order;
            }
        }
        return new ArrayList<Double>(Arrays.asList(movingArray));         
    }
   
    /// <summary>
    /// 加权移动平均
    /// 加权移动平均阶数应为大于0的奇数。如果输入阶数小于等于0，返回输入序列。如果阶数为偶数，返回移动平均结果。
    /// 加权移动平均权重序列长度应等于加权移动平均阶数。如果不等，则返回输入序列。
    /// </summary>
    /// <param name="_order">加权移动平均阶数</param>
    /// <param name="dataArrayList">输入序列</param>
    /// <param name="weightArrayList">加权移动平均权重序列</param>
    /// <returns></returns>
    public ArrayList<Double> MAverWithWeight(int order, ArrayList<Double> dataArrayList ,ArrayList<Double> weightArrayList)
    {
        if (order <= 0) return dataArrayList;
        if (order != weightArrayList.size()) return dataArrayList;
        ArrayList<Double> extendArrayList = new ArrayList<Double>();
        Double[] array = (Double[]) dataArrayList.toArray();           
        Double a=(order-1.0)/2.0;
        int halfOrder =(int) Math.floor(a);
        boolean isEvenOrder = false; //是否是偶数阶
        if (order / 2 - halfOrder > 0) { halfOrder += 1; isEvenOrder = true; }
        
        int arrayLength=array.length;
        Double[] extendArray = new Double[arrayLength + order];
        Double[] movingArray = new Double[arrayLength];
        extendArray[0] = 0.0;
        for (int i = 0; i < halfOrder; i++) //增加序列长度
        {
            for (int j = 0; j < halfOrder; j++)
            {
            	extendArray[i] += array[j];
                extendArray[arrayLength + i+halfOrder] += array[arrayLength -halfOrder+j]; 
            }
            extendArray[i] = extendArray[i] / halfOrder;
            extendArray[arrayLength + i+ halfOrder] = extendArray[arrayLength + halfOrder + i] / halfOrder;
        }
        for (int i = 0; i < arrayLength; i++)
        {
            extendArray[halfOrder + i] = array[i];
        }

        if (isEvenOrder) //是偶数阶
        {
            for (int i = 0; i < arrayLength; i++)
            {
                for (int j = 0; j < order; j++)
                    if (j == 0 || j == order)
                        movingArray[i] += extendArray[i + j] / 2;
                    else
                        movingArray[i] += extendArray[i + j];
                movingArray[i] = movingArray[i] / order;
            }
        }
        else //是奇数阶
        {
            for (int i = 0; i < arrayLength; i++)
            {
                for (int j = 0; j < order; j++)
                    movingArray[i] += extendArray[i+j] * weightArrayList.get(j);
                
            }
        }
        return new ArrayList<Double>(Arrays.asList(movingArray));
    }
  
    /// <summary>
    /// 求序列的极大值和极小值点。
    /// 返回整型二维序列。
    /// 最大值以1标示，最小值以0标示；用该极值点的坐标标示该极值点。
    /// </summary>
    /// <param name="dataArrayList">输入序列</param>
    /// <returns>返回整型二维序列。最大值以1标示，最小值以0标示；用该极值点的坐标标示该极值点。</returns>
    public int[][] FindPV(ArrayList<Double> dataArrayList)
    {
        Double[] array = (Double[]) dataArrayList.toArray();
        int arrayLength = array.length;
        int TableNum = -1;
        int[][] Table1 = new int[arrayLength][2];

        for (int i = 1; i < arrayLength - 1; i++)
        {
            if ((array[i] - array[i - 1]) * (array[i] - array[i + 1]) >= 0)
            {
                TableNum += 1;
                Table1[TableNum][0] = i;
                if ((array[i] >= array[i - 1]) & (array[i] >= array[i + 1]))
                    Table1[TableNum][1] = 1;
                else
                    Table1[TableNum][1] = 0;
            }
        }

        int[][] Table2 = new int[TableNum + 1][2];
        for (int i = 0; i <= TableNum; i++)
        {
            Table2[i][0] = Table1[i][0];
            Table2[i][1] = Table1[i][1];
        }
        return Table2;
    }

   /// <summary>
    ///  筛选峰谷
    /// 求输入的峰谷序列加上限制条件后得到的峰谷序列
   /// </summary>
    /// <param name="PVSeries">输入的峰谷序列</param>
    /// <param name="halfPeriod">峰谷间差距，推荐为6个月或2个季度</param>
    /// <param name="onePeriod">峰峰，谷谷之间差距，推荐为15个月或5个季度</param>
    /// <returns>返回整型二维序列。最大值以1标示，最小值以0标示；用该极值点的坐标标示该极值点。</returns>
    public int[][] FindConstriantPV(int[][] PVSeries, int halfPeriod, int onePeriod)
    {
        if (halfPeriod < 0 || onePeriod < 0 || halfPeriod > onePeriod) return null;
        int TableLength = PVSeries.length;
        int[][] Table2 = new int[TableLength][2];
        int[][] Table3 = new int[TableLength][2];

        int a, b, c, d;
        a = c = 0;
        b = d = -1;

        while (a < TableLength - 1)
        {
            if (PVSeries[a + 1][0] - PVSeries[a][0] >= halfPeriod)
            {
                b = b + 1;
                Table2[b][0] = PVSeries[a][0];
                Table2[b][1] = PVSeries[a][1];
                a = a + 1;
            }
            else
            {
                a = a + 2;
            }
        }
        if (a == TableLength - 1)
        {
            b = b + 1;
            Table2[b][0] = PVSeries[a][0];
            Table2[b][1] = PVSeries[a][1];
        }

        while (c < b - 1)
        {
            if (Table2[c + 2][0] - Table2[c][0] >= onePeriod)
            {
                d = d + 1;
                Table3[d][0] = Table2[c][0];
                Table3[d][1] = Table2[c][1];
                c = c + 1;
            }
            else
            {
                c = c + 2;
            }
        }
        if (c == b - 1)
        {
            d = d + 1;
            Table3[d][0] = Table2[c][0];
            Table3[d][1] = Table2[c][1];
            d = d + 1;
            c = c + 1;
            Table3[d][0] = Table2[c][0];
            Table3[d][1] = Table2[c][1];
        }

        int[][] Table4 = new int[d + 1][2];
        for (int i = 0; i <= d; i++)
        {
            Table4[i][0] = Table3[i][0];
            Table4[i][1] = Table3[i][1];
        }

        return Table4;
    }

    /// <summary>
    /// /从峰谷的r邻域中重新确定峰谷
    /// </summary>
    /// <param name="PVSeries">峰谷序列，为int[][]，其中第一维为峰/谷标志位(峰1谷0)，第二位为该峰/谷在原序列中的坐标</param>
    /// <param name="MCD">输入序列的MCD值</param>
    ///  <param name="dataArrayList">输入序列</param>
    /// <returns>返回整型二维序列。最大值以1标示，最小值以0标示；用该极值点的坐标标示该极值点。</returns>
    public int[][] AdjustPV(int[][] PVSeries,int MCD,ArrayList<Double> dataArrayList)
	{

        Double[] array = (Double[]) dataArrayList.toArray();
        int r;
		if(MCD<=4)		r=4;
		else	r=MCD;
		
		int TableLength=PVSeries.length;
		int ArrayLength=array.length;
		int[][] Table2=new int[TableLength][2];

		for(int i=0;i<TableLength;i++)
		{
			int InfBound,SupBound;
			InfBound=PVSeries[i][0]<r?0:(PVSeries[i][0]-r);
			SupBound=((PVSeries[i][0]+r)>(ArrayLength-1))?(ArrayLength-1):(PVSeries[i][0]+r);
			
			int mpos=PVSeries[i][0];
			Double mnum=array[PVSeries[i][0]];

			if(PVSeries[i][1]==1)
			{
				for(int j=InfBound;j<=SupBound;j++)
				{
					if(array[j]>mnum)
					{
						mpos=j;
						mnum=array[j];
					}
				}
			}
			else
			{
				for(int j=InfBound;j<=SupBound;j++)
				{
					if(array[j]<mnum)
					{
						mpos=j;
						mnum=array[j];
					}	
				}
			}

			Table2[i][0]=mpos;
			Table2[i][1]=PVSeries[i][1];
		}
		return Table2;
	}

  /// <summary>
   /// 消除特异值
   /// 特异值定义为落在序列均值两个标准差之外的数值
   /// 用序列Spencer移动平均后对应的值替换特异值
   /// </summary>
  /// </summary>
  /// <param name="array">输入序列</param>
  /// <returns></returns>
    public ArrayList<Double> RemoveOutlier(ArrayList<Double> array)
    {

        ArrayList<Double> newArray = new ArrayList<Double>();
        int A = 2;///the outlier parameter
        Double mean = CalculateMean(array);
        Double std = CalculateStdev(array);
        ArrayList<Double> spencerArray = DoSpencer(array);
        for (int i = 0; i < array.size(); i++)
        {
            if (array.get(i) < mean - A * std || array.get(i) > mean + A * std)
                newArray.add(spencerArray.get(i));
            else
                newArray.add(array.get(i));
        }

        return newArray;
    }
    
    /// <summary>
    /// 对日期进行加法的操作
    /// 只能计算月度数据
    /// 例子：AddMonth(“1997-01”,3)，返回1997-04
    /// </summary>
    /// <param name="start">起始日期，格式如：YYYY-MM</param>
    /// <param name="i">要加的月份</param>
    /// <returns></returns>
    public String AddMonth(String start, int i)
    {
        try
        {

            Integer year = Integer.parseInt(start.substring(0, 4));
            Integer month = Integer.parseInt(start.substring(5, 2));
            int addYear = (int)Math.floor((double)(i / 12));
            int addMonth = i - addYear * 12;
            if (month + addMonth <= 12)
            {
                Integer resultYear = addYear + year;
                Integer resultMonth = month + addMonth;
                if (resultMonth < 10)
                    return resultYear.toString() + "-0" + resultMonth.toString();
                else
                    return resultYear.toString() + "-" + resultMonth.toString();
            }
            else
            {
                Integer resultYear = addYear + year+1;
                Integer resultMonth = month + addMonth-12;
                if (resultMonth < 10)
                    return resultYear.toString() + "-0" + resultMonth.toString();
                else
                    return resultYear.toString() + "-" + resultMonth.toString();

            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        
    }

   /// <summary>
   /// 两个月度日期之间相隔的月数
   /// 第一个日期与第二个日期相差的月数
    /// 例：GetDatesDiff("1999-12","1998-12") 得到12
    /// 例：GetDatesDiff("1998-12","1999-12") 得到-12
   /// </summary>
   /// <param name="date1">起始日期，格式如：YYYY-MM</param>
    /// <param name="date2">终止日期，格式如：YYYY-MM</param>
   /// <returns></returns>
    public int GetDatesDiff(String date1, String date2)
    {
        try
        {

            int year1 = Integer.parseInt(date1.substring(0, 4));
            int month1 = Integer.parseInt(date1.substring(5, 2));
            int year2 = Integer.parseInt(date2.substring(0, 4));
            int month2 = Integer.parseInt(date2.substring(5, 2));
            return year1 * 12 + month1 - year2 * 12 - month2; 
        }
        catch (Exception ex)
        {
            throw ex;
        }

    }
}
